# coding=utf-8
import os
from flask_restful import Resource
from flask import current_app,request,g
from comment.modules.user import User
from comment.modules.account import Account
from comment.ultils.aliyun_sms_send import send_sms
import json
from comment.ultils.Financlal_Readlis import fr #Redis数据库的连接
from financial.resorces.account import AccountInfoSerializer
from financial.resorces.user import constants #导入整个py
from flask_restful.reqparse import RequestParser #通过这个来接收数据
from comment.modules.user import db
from comment.ultils.tokens_pyjwt import generate_tokens,verify_tokens
from comment.ultils.decorators import Login_required
import uuid
from financial.resorces.user.serializer import InvitedListSerializer, UserInfoSerializer
from financial.resorces.transaction.const import LoanConfig

#AxiosText的请求资源
# class AxiosText(Resource):
#     def post(self):
#         r = request
#         data = r.json.get('id')
#         return {'msg':'调用了一个post方法'}
#
#     def get(self):
#         r = request
#         id = r.args.get('id')
#         return {'msg':'调用了一个get方法'}

class Login(Resource):
    '''登录'''
    def post(self):
        rp = RequestParser()
        rp.add_argument('username',required=True) #他是前端传过来的,required是必须要传的（验证规则）
        rp.add_argument('password',required=True)

        args = rp.parse_args() #得到所有参数的字典

        # 接收参数
        username = args.username #用户名
        password = args.password #密码

        #根据接收到的数据到数据库中查找对应的对象（数据库中是没有存密码的，所以千万不要拿着密码到数据库中查（数据库中存的是密文））
        user = User.query.filter(User.username == username).first()
        if user:
            #验证密码是否正确（判断加密后的密文是否正确）
            if user.check_password(password):
                #用户登录成功，之后生成一个token，以便后面去做认证token
                token = generate_tokens(user.id)

                #测试和验证token
                # current_app.logger.info(token)
                # current_app.logger.info('测试验证token',verify_tokens(token))

                return {'token':token} #把token返回出去
        return {
            'message':'用户名或者密码错误',
            'code': 207
        }

class LoginOut(Resource):


    '''拦截器看用户有没有登录过'''
    #拦截器他会在LoginOut的所有的视图函数都会在执行视图函数之前，做一下装饰器的拦截
    method_decorators = [Login_required] #使用了登录拦截的装饰器

    # 如果只想在某一个视图函数中用登录拦截，其他不用，或者用别的登录拦截的话：
    # method_decorators = { #method_decorators始终不变
    #     'post':[Login_required], #前面是视图函数的名字，后面用[]里面写登录拦截的装饰器
    #     'get':[Login_required()] #这里表示每一个函数使用不同的装饰器
    # }
    # 如果想当前类所有视图函数使用的话，就用上面的第一种

    '''退出登录'''
    def post(self):
        if g.user_id:
            g.user_id = None
        return {'msg':'成功退出登录'}

class UserAvatar(Resource):
    '''关联用户的头像'''
    method_decorators = [Login_required] #使用了登录拦截的装饰器

    def post(self):
        #上传用户头像

        #用户上传的图片数据
        img_data = request.files['file'] #files得到用户整个上传的文件，他是一个字典，用文件的名字得到上传的文件

        id = g.user_id
        user = User.query.filter(User.id == id).first()

        #设置用户头像图片保
        img_dir = current_app.config['AVATAR_DIR']

        #设置文件名（为了防止文件重名，用用户名来取名）
        img_name = str(id) + '_' + img_data.filename

        #文件路径
        file_path = img_dir + '\\' + img_name

        try:
            #保存文件
            img_data.save(file_path)
        except Exception as e:
            return {
                'message':'头像上传失败',
                'code':213
            }
        #在数据库中保存用户头像图片名
        if user:
            user.avatar = img_name
            db.session.commit()
            return {
                'msg':'上传头像图片成功',
                'avatar':img_name
            }
class RegisterUser(Resource):
    '''用户的注册'''
    def post(self): #一定是post，因为表示表单提交
        rp = RequestParser()
        # 他是前端传过来的,required是必须要传的（验证规则）
        rp.add_argument('phone',required=True) #手机号
        rp.add_argument('username', required=True) #用户名
        rp.add_argument('password', required=True) #密码
        rp.add_argument('code', required=True) #验证码
        rp.add_argument('invite_code') #邀请码（可写可不写，所以不用传required）

        args = rp.parse_args() #得到所有参数的字典

        #接收参数
        username = args.username
        password = args.password
        phone = args.phone
        code = args.code
        invite_code = args.invite_code

        #验证用户名是否唯一
        u = User.query.filter(User.username == username).first()
        if u: #用户名存在
            current_app.logger.info("{}:用户名已经存在，请更换用户名".format(username))
            return {'message':'用户名重复','code':203}

        # 从Redis数据库中获取之前的验证码
        real_code = fr.get('registerCode:{}'.format(phone))
        if not real_code or real_code.decode() != code: #数据库中没有code的，或者存储的code和参数不一致（有可能是错误或者过期了）
            current_app.logger.info("验证码错误或者失效")
            return {'message': '验证码错误或者失效', 'code': 203}

        #把用户保存到数据库中（密码是通过加密的，所以是函数=现在的密码）
        u = User(username=username,phone=phone,pwd=password)

        #验证和关联邀请码
        if invite_code:
            self.check_invite(u,invite_code)

        try: #需要用到数据库事务处理
            db.session.add(u) #将数据添加到数据库中 #到这里之前都没有插入数据，因为还没有commit
            #必须要用，不用的话就没有u.id
            db.session.flush() #把数据插入数据库的缓冲区（得到自增的ID），并不是真正的插入数据库中
            account = Account(userId=u.id) #创建当前用户的账户对象
            db.session.add(account)
            db.session.commit()
            return {'msg':'success'}
        except Exception as ex:
            current_app.logger.error(ex) #填写日志
            db.session.rollback() #第二步没有成功，就到第一步回滚
            return {
                'message': '用户注册时插入数据库失败',  # 提示的内容
                'code': 204  # 状态码
            }

    #验证和关联邀请码
    def check_invite(self,user,invte_code):
        code = invte_code.strip()
        invite_user = User.query.filter(User.inviteId == code).first()
        if invite_user:
            user.invite_user_id = invite_user.id #如果邀请码有效，则把这两个用户关联一下
            invite_user.accountInfo.discount += constants.INVITE_MONKEY #邀请用户的账户增加50的代金券
            invite_user.sumFriends = invite_user.sumFriends +1 if invite_user.sumFriends else 1  # 邀请的记录数 增加一个
class IsExistPhone(Resource):
    '''判断手机号是否存在'''
    def post(self): #如果是post请求，请求的参数会封装成一个json对象
        phone = request.json.get('phone')
        user = User.query.filter(User.phone == phone).first()
        if user:
            return {
                'message':'此手机号已经注册过了，请更换手机号', #提示的内容
                'code':201 #状态码
            }
        return {'msg':'success'} #这是正常的状态码

class SmsCode(Resource):
    '''发送手机短信，验证手机号码'''
    def get(self): #如果是get请求，请求的参数是： 参数名=参数
        #他拿到的是字符串，所以用strip去掉空格
        phone = request.args['phone'].strip() #request.args是拿到所有的参数，[]是拿到里面的值，strip()是去掉空格

        #生产随机的验证码
        import random
        code = random.randint(1000,9999)

        result = send_sms(phone,str(code)) #调用短信平台

        re_dict = result
        if re_dict['Code'] == 'OK':
            current_app.logger.info('给手机号为：{}发送短信成功'.format(phone))
            #验证码需要存起来，存放到Redis，还需要把验证码在注册的请求中验证
            #setex函数有三个参数：1.key 2.存放的失效 3.values
            fr.setex('registerCode:{}'.format(phone),constants.SMS_CODE_EXPIRES,code) #把验证码按照字节的格式存放到Redis中

            return {'msg':'success','sms_Code':code} #给前端数据

        else:
            return {
                'message': '此手机号发送短信失败',  # 提示的内容
                'code': 202  # 状态码
            }

class Invite(Resource):

    method_decorators = [Login_required] #使用了登录拦截的装饰器

    '''邀请码奖励管理的资源'''
    def post(self):
        '''生成邀请码'''
        u_id = g.user_id
        user = User.query.filter(User.id == u_id).first()
        #根据用户名，采用UUID算法生成一个唯一的邀请码
        invit_code = str(uuid.uuid5(uuid.NAMESPACE_DNS,user.username))
        user.inviteId = invit_code #保存到用户数据库里面去
        db.session.commit()
        return {
            'msg':'success',
            'invit_code':invit_code
        }

    def get(self):
        '''查询被邀请人的列表'''
        u_id = g.user_id

        #查询被邀请的人
        invied_list = User.query.filter(User.invite_user_id == u_id).all()
        if invied_list:
            data = InvitedListSerializer(invied_list).to_dict()
            return {
                'msg':'success',
                'list':data
            }
        else:
            return {
                'msg': 'success',
                'list': []
            }

class UserInfo(Resource):
    '''用户信息资源类'''

    method_decorators = [Login_required] #使用了登录拦截的装饰器

    def get(self):
        '''当前用户的查询，包括是关联的账号信息'''
        u_id = g.user_id
        user = User.query.filter(User.id == u_id).first()
        user_info_data = UserInfoSerializer(user).to_dict()
        #在查询当前用户所对应的账户信息
        acc = Account.query.filter(Account.userId == u_id).first()

        acc_date = {}
        if acc:
            acc_date = AccountInfoSerializer(acc).to_dict()
        return {
            'roles':['admin'] if user.role else ['user'],
            'name':user.username,
            'userInfoDate':user_info_data,
            'accountInfo':acc_date
            #'loanconfig':LoanConfig.YEAR_RATE.value
        }
class RealNameAuth(Resource):
    """
    实名认证
    """
    # 拦截器他会在LoginOut的所有的视图函数都会在执行视图函数之前，做一下装饰器的拦截
    method_decorators = [Login_required]  # 使用了登录拦截的装饰器

    def post(self):
        rp = RequestParser()
        rp.add_argument('realName', required=True)
        rp.add_argument('idNum', required=True)
        args = rp.parse_args()
        realName = args.realName   # 真实姓名
        idNum = args.idNum         # 身份证ID
        id = g.user_id
        user = User.query.filter(User.id == id).first()
        if user.realName:
            return {"code": 20001, 'message': '不可重复验证'}
        else:
            user.realName = realName  # 名字
            user.realNameStatus = 1  # 实名状态
            user.idNum = idNum  # 身份证
            db.session.commit()
            return {'msg': 'success'}

class ChangePhone(Resource):
    '''更换手机号'''
    # 拦截器他会在LoginOut的所有的视图函数都会在执行视图函数之前，做一下装饰器的拦截
    method_decorators = [Login_required]  # 使用了登录拦截的装饰器

    def post(self):
        rp = RequestParser()
        #location从哪里去数据
        rp.add_argument('oldPhone',required=True, location="json")
        #rp.add_argument('old_phone_code',required=True)
        rp.add_argument('newPhone', required=True, location="json")
        #rp.add_argument('new_phone_code',required=True)

        args = rp.parse_args()

        old_phone = args.oldPhone
        #old_phone_code = args.old_phone_code
        new_phone = args.newPhone
        #new_phone_code = args.new_phone_code

        id = g.user_id

        user = User.query.filter(User.id == id).first()
        if user:
            if user.phone == old_phone:
                # #旧手机的验证
                # try:
                #     real_code = fr.get('registerCode:{}'.format(user.phone))
                #     if not real_code or real_code.decode() != old_phone_code:  # 数据库中没有code的，或者存储的code和参数不一致（有可能是错误或者过期了）
                #         current_app.logger.info("验证码错误或者失效")
                #         return {'message': '验证码错误或者实效', 'code': 203}
                # except Exception as e:
                #     current_app.logger.error(e)
                #     return {
                #         'message': '旧手机验证码判断的时候，数据库连接错误',
                #         'code': 214
                #     }
                # # 新手机的验证
                # try:
                #     real_code = fr.get('registerCode:{}'.format(new_phone))
                #     if not real_code or real_code.decode() != new_phone_code:  # 数据库中没有code的，或者存储的code和参数不一致（有可能是错误或者过期了）
                #         current_app.logger.info("验证码错误或者失效")
                #         return {'message': '验证码错误或者实效', 'code': 203}
                # except Exception as e:
                #     current_app.logger.error(e)
                #     return {
                #         'message': '新手机验证码判断的时候，数据库连接错误',
                #         'code': 214
                #     }

                #更改手机号
                user.phone = new_phone

                #提交数据
                db.session.commit()
                return {'msg': 'success'}

            else:
                return {"code": 222, 'message': '更换手机号失败'}

class Changing_the_Login_Password(Resource):
    '修改登录密码'
    # 拦截器他会在LoginOut的所有的视图函数都会在执行视图函数之前，做一下装饰器的拦截
    method_decorators = [Login_required]  # 使用了登录拦截的装饰器

    def post(self):
        rp = RequestParser()
        rp.add_argument('old_passworld', required=True, location="json")  # 旧密码
        rp.add_argument('new_passworld', required=True, location="json")  # 新密码
        rp.add_argument('code', required=True, location="json")  # 验证码

        args = rp.parse_args()

        old_passworld = args.old_passworld
        new_passworld = args.new_passworld
        code = args.code

        user_id = g.user_id
        #查看当前用户
        user = User.query.filter(User.id == user_id).first()
        if user: # 如果有数据的话
            if  user.check_pay_password(old_passworld): #如果登录密码不一样的话
                return {  # 登录密码错误则返回
                    'message': '旧的登录密码错误',
                    'code': 223
                }
            else:
                # 手机号验证码
                try:
                    real_code = fr.get('registerCode:{}'.format(user.phone))
                    if not real_code or real_code.decode() != code:  # 数据库中没有code的，或者存储的code和参数不一致（有可能是错误或者过期了）
                        current_app.logger.info("验证码错误或者失效")
                        return {'message': '验证码错误或者失效', 'code': 203}
                except Exception as e:
                    current_app.logger.error(e)
                    return {
                        'message': '新手机验证码判断的时候，数据库连接错误',
                        'code': 214
                    }
                #修改数据
                user.pay_pwd = new_passworld
                user.password = user.pay_pwd

                # 提交数据
                db.session.commit()
                return {'msg': 'success'}
class Set_a_payment_password(Resource):
    ' 设置支付密码 '

    # 拦截器他会在LoginOut的所有的视图函数都会在执行视图函数之前，做一下装饰器的拦截
    method_decorators = [Login_required]  # 使用了登录拦截的装饰器
    def post(self):
        rp = RequestParser()

        rp.add_argument('code', required=True)  # 验证码
        rp.add_argument('payment_password', required=True, location="json")  # 支付密码

        args = rp.parse_args()

        #接收参数
        code = args.code
        payment_password = args.payment_password

        user_id = g.user_id

        # 查询当前登录的用户
        user = User.query.filter(User.id == user_id).first()
        if user:
            # 手机号验证码
            try:
                real_code = fr.get('registerCode:{}'.format(user.phone))
                if not real_code or real_code.decode() != code:  # 数据库中没有code的，或者存储的code和参数不一致（有可能是错误或者过期了）
                    current_app.logger.info("验证码错误")
                    return {'message': '验证码错误', 'code': 203}
            except Exception as e:
                current_app.logger.error(e)
                return {
                    'message': '新手机验证码判断的时候，数据库连接错误',
                    'code': 214
                }
            user.pay_pwd = payment_password
            # 将支付密码插入数据库
            user.payPassword = user.pay_pwd
            # 将支付密码状态进行修改
            user.payPwdStatus = 1

            db.session.commit()
            return {'msg': 'success'}

class Change_payment_PIN(Resource):
    ' 更换支付密码 '

    # 拦截器他会在LoginOut的所有的视图函数都会在执行视图函数之前，做一下装饰器的拦截
    method_decorators = [Login_required]  # 使用了登录拦截的装饰器
    def post(self):
        rp = RequestParser()

        rp.add_argument('ChangePIN_old_passworld', required=True, location="json")  # 旧支付密码
        rp.add_argument('ChangePIN_new_passworld', required=True, location="json")  # 新支付密码
        rp.add_argument('ChangePIN_code', required=True)  # 验证码

        args = rp.parse_args()

        #接收参数
        ChangePIN_old_passworld = args.ChangePIN_old_passworld
        ChangePIN_new_passworld = args.ChangePIN_new_passworld
        ChangePIN_code = args.ChangePIN_code

        user_id = g.user_id

        # 查询当前登录的用户
        user = User.query.filter(User.id == user_id).first()
        if user:
            if not user.check_pay_password(ChangePIN_old_passworld): #如果支付密码和原来的一样的话
                return {
                    'message': '不可以和旧支付密码相同',
                    'code': 224
                }
            else:
                # 手机号验证码
                try:
                    real_code = fr.get('registerCode:{}'.format(user.phone))
                    if not real_code or real_code.decode() != ChangePIN_code:  # 数据库中没有code的，或者存储的code和参数不一致（有可能是错误或者过期了）
                        current_app.logger.info("验证码错误")
                        return {'message': '验证码错误', 'code': 203}
                except Exception as e:
                    current_app.logger.error(e)
                    return {
                        'message': '新手机验证码判断的时候，数据库连接错误',
                        'code': 214
                    }

                # 修改支付密码
                user.pay_pwd = ChangePIN_new_passworld
                user.payPassword = user.pay_pwd

                db.session.commit()
                return {'msg': 'success'}